package org.db2.project.airline.service;

import org.db2.project.airline.dao.*;
import org.db2.project.airline.dao.impl.*;
import org.db2.project.airline.dto.BookingDetails;
import org.db2.project.airline.model.*;
import org.db2.project.airline.utils.HibernateHelper;

import java.math.BigDecimal;

/**
 *
 */
public class BookingService {

    private AirplaneDAO airplaneDAO = new HibernateAirplaneDAO();

    private final FlightClassDAO flightClassDAO = new HibernateFlightClassDAO();

    private final FlightClassPriceDAO flightClassPriceDAO = new HibernateFlightClassPriceDAO();

    private final ClientDAO clientDAO = new HibernateClientDAO();

    private final BookingStatusDAO bookingStatusDAO = new HibernateBookingStatusDAO();

    private final BookingDAO bookingDAO = new HibernateBookingDAO();

    private final BookingSeatDAO bookingSeatDAO = new HibernateBookingSeatDAO();

    public BookingService() {
        // empty constructor
    }

    public BigDecimal getBookingPrice(FlightPlan flightPlan, BookingDetails bookingDetails,
                                      FlightClassType flightClassType) throws DAOException {

        FlightClassPrice flightClassPrice;

        try {
            HibernateHelper.beginTransaction();

            flightClassPrice = flightClassPriceDAO.find(flightPlan, flightClassType);

            HibernateHelper.commitTransaction();

            return flightClassPrice.getPrice() != null ?
                    flightClassPrice.getPrice().add(new BigDecimal(bookingDetails.getAmount())) : null;
        } catch (DAOException exc) {
            HibernateHelper.rollbackTransaction();

            throw exc;
        }
    }

    public long bookSeatsOneWay(FlightPlan departureFlight, BookingDetails bookingDetails, FlightClassType flightClassType) throws DAOException {

        try {
            HibernateHelper.beginTransaction();

            long id = bookSeats(departureFlight, bookingDetails, flightClassType);

            HibernateHelper.commitTransaction();

            return id;
        } catch (DAOException e) {
            HibernateHelper.rollbackTransaction();

            throw e;
        }
    }

    public long[] bookSeatsTwoWay(FlightPlan departureFlight, FlightPlan arrivalFlight, BookingDetails bookingDetails, FlightClassType flightClassType) throws DAOException {

        long[] ids = new long[2];

        try {
            HibernateHelper.beginTransaction();

            ids[0] = bookSeats(departureFlight, bookingDetails, flightClassType);
            ids[1] = bookSeats(arrivalFlight, bookingDetails, flightClassType);

            HibernateHelper.commitTransaction();

            return ids;
        } catch (DAOException e) {
            HibernateHelper.rollbackTransaction();

            throw e;
        }
    }

    public long bookSeats(FlightPlan flightPlan, BookingDetails bookingDetails, FlightClassType flightClassType)
            throws DAOException {

        Airplane airplane;
        Client client;
        BookingStatus bookingStatus;
        Booking booking;
        FlightClass flightClass;

        // retrieves client information
        try {
            client = getOrCreateClient(bookingDetails);

            // retrieves the booking status
            bookingStatus = getBookingStatus();

            // creates booking and saves it in persistence
            booking = createBooking(flightPlan, client, bookingStatus);

            flightClass = flightClassDAO.find(flightPlan, flightClassType);

            airplane = airplaneDAO.findAirplane(flightPlan);

            // TODO retrieve the 'nextSeatId'
            // TODO retrieve number of seats in class, to check if there enough seats
            long nextSeatId = bookingSeatDAO.getMaxSeatId(flightClass);

            long maxSeats = airplane.getSeatsInRaw() *
                    (flightClass.getEndingSeatRow() - flightClass.getStartingSeatRow() + 1);
            if ((flightClass.getStartingSeatRow() - 1) * airplane.getSeatsInRaw() + maxSeats
                    < (nextSeatId + bookingDetails.getAmount())) {
                throw new DAOException("Could not book seats, the airplane is full.");
            }

            // creates booking seats and saves them
            BookingSeat bookingSeat;
            for (int i = 0; i < bookingDetails.getAmount(); i++) {
                bookingSeat = new BookingSeat();
                bookingSeat.setSeat(++nextSeatId);
                bookingSeat.setBooking(booking);
                bookingSeat.setFlightClass(flightClass);

                bookingSeatDAO.save(bookingSeat);
            }

            return booking.getId();
        } catch (DAOException e) {

            throw e;
        }
    }

    private Booking createBooking(FlightPlan flightPlan, Client client, BookingStatus bookingStatus) throws DAOException {
        Booking booking = new Booking();
        booking.setClient(client);
        booking.setFlightPlan(flightPlan);
        booking.setBookingStatus(bookingStatus);
        booking.setClient(client);

        bookingDAO.save(booking);
        return booking;
    }

    private BookingStatus getBookingStatus() throws DAOException {
        BookingStatus bookingStatus;
        bookingStatus = bookingStatusDAO.findByStatusName("zarezerwowane");
        // TODO check if bookingStatus has been set
        if (bookingStatus == null) {
            throw new DAOException("Nie można pobrać stanu dla rezerwacji");
        }
        return bookingStatus;
    }

    private Client getOrCreateClient(BookingDetails bookingDetails) throws DAOException {
        Client client;
        client = clientDAO.findByEmail(bookingDetails.getClientEmail());

        if (client == null) {
            client = new Client();
            client.setName(bookingDetails.getClientFirstName() + " " + bookingDetails.getClientLastName());
            client.setAddress(bookingDetails.getClientAddress());
            client.setTelephone(bookingDetails.getClientPhoneNumber());
            client.setNationality(bookingDetails.getClientNationality());
            client.setEmail(bookingDetails.getClientEmail());

            clientDAO.save(client);
        }
        return client;
    }
}
